// The skeleton of this source code is from http://thinkandroid.wordpress.com/2010/01/13/writing-your-own-contentprovider/

package se.peterbjorkman.android.trafikkamera.providers;

import java.util.HashMap;

import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.util.Log;

public class CameraContentProvider extends ContentProvider {

	private static final String TAG = "CameraContentProvider";
	
	public static final String CONTENT_TYPE = "vnd.android.cursor.dir/vnd.trafikkamera.cameras ";

	private static HashMap<String, String> camerasProjectionMap;

	public static final Uri CONTENT_URI = Uri.parse("content://" + CameraDefs.AUTHORITY
			+ "/" + CameraDefs.CAMERAS);

	public static final Uri COUNTY_URI = Uri.parse("content://" + CameraDefs.AUTHORITY
			+ "/" + CameraDefs.COUNTIES);

	public static final Uri ROUTES_URI = Uri.parse("content://" + CameraDefs.AUTHORITY
			+ "/" + CameraDefs.ROUTES);

	public static final Uri CAMERAROUTES_URI = Uri.parse("content://" + CameraDefs.AUTHORITY
			+ "/" + CameraDefs.CAMERAROUTES);

	private static final UriMatcher sUriMatcher;

	private static final int CAMERAS = 1;
	private static final int COUNTIES = CAMERAS + 1;
	private static final int ROUTES = COUNTIES + 1;
	private static final int CAMERAROUTES = ROUTES + 1;

	private static class DatabaseHelper extends SQLiteOpenHelper {

		DatabaseHelper(Context context) {
			super(context, DatabaseDefs.DATABASE_NAME, null, DatabaseDefs.DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			db.execSQL("CREATE TABLE " + DatabaseDefs.CAMERAS_TABLE_NAME + " (" + 
					DatabaseDefs.ID               + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
					DatabaseDefs.NAME             + " text    NOT NULL," + 
					DatabaseDefs.COUNTY           + " integer     NULL," +
					DatabaseDefs.ROAD             + " text        NULL," +
					DatabaseDefs.DESCRIPTION      + " text        NULL," + 
					DatabaseDefs.CAMERA_URL       + " text    NOT NULL," + 
					DatabaseDefs.MAP_URL          + " text        NULL," + 
					DatabaseDefs.LONGITUDE        + " real        NULL," + 
					DatabaseDefs.LATITUDE         + " real        NULL," +  
					DatabaseDefs.EXTID            + " text    NOT NULL," +  
					DatabaseDefs.CAMERA_URL_SMALL + " text        NULL," +  
					DatabaseDefs.NICKNAME         + " text        NULL," +  
					DatabaseDefs.FAVORITE         + " bit     NOT NULL Default 0" + ");");
			
			db.execSQL("CREATE TABLE " + DatabaseDefs.COUNTY_TABLE_NAME + " (" + 
					DatabaseDefs.ID               + " INTEGER PRIMARY KEY," + 
					DatabaseDefs.NAME             + " text    NOT NULL);");
			insertCounty("Västra Götalands län", 14, db);
			insertCounty("Norrbottens län", 25, db);
			insertCounty("Västerbottens län", 24, db);
			insertCounty("Västernorrlands län", 22, db);
			insertCounty("Gävleborgs län", 21, db);
			insertCounty("Jämtlands län", 23, db);
			insertCounty("Dalarnas län", 20, db);
			insertCounty("Värmlands län", 17, db);
			insertCounty("Västmanlands län", 19, db);
			insertCounty("Örebro län", 19, db);
			insertCounty("Östergötlands län", 5, db);
			insertCounty("Kalmar län", 8, db);
			insertCounty("Blekinge län", 10, db);
			insertCounty("Hallands län", 13, db);
			insertCounty("Uppsala län", 3, db);
			insertCounty("Stockholms län", 2, db);
			insertCounty("Södermanlands län", 4, db);
			insertCounty("Skåne län", 12, db);
			insertCounty("Jönköpings län", 6, db);
			insertCounty("Kronobergs län", 7, db);
			insertCounty("Gotlands län", 9, db);

			db.execSQL("CREATE TABLE " + DatabaseDefs.ROUTES_TABLE_NAME + " (" + 
					DatabaseDefs.ID               + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
					DatabaseDefs.NAME             + " text    NOT NULL);");

			db.execSQL("CREATE TABLE " + DatabaseDefs.CAMERASINROUTE_TABLE_NAME + " (" + 
					DatabaseDefs.ID               + " INTEGER PRIMARY KEY AUTOINCREMENT," + 
					DatabaseDefs.ROUTEID          + " INTEGER    NOT NULL, " +
					DatabaseDefs.CAMERAID         + " INTEGER    NOT NULL, " +
					DatabaseDefs.SORTORDER        + " INTEGER    NOT NULL);");

			// TODO Debug hardcoded values
			long firstid  = insertRoute("En rutt", db);
			long secondid = insertRoute("En annan rutt", db);
			insertCameraInRoute(firstid,  1, db, 1);
			insertCameraInRoute(firstid,  2, db, 2);
			insertCameraInRoute(secondid, 3, db, 1);
			insertCameraInRoute(secondid, 4, db, 2);
		}

		private void insertCounty(String name, int id, SQLiteDatabase db) {
			ContentValues values = new ContentValues();
			values.put(DatabaseDefs.ID, id);
			values.put(DatabaseDefs.NAME, name);
			db.insert(DatabaseDefs.COUNTY_TABLE_NAME, null, values);
		}

		private long insertRoute(String name, SQLiteDatabase db) {
			ContentValues values = new ContentValues();
			values.put(DatabaseDefs.NAME, name);
			return db.insert(DatabaseDefs.ROUTES_TABLE_NAME, null, values);
		}

		private void insertCameraInRoute(long routeId, long cameraId, SQLiteDatabase db, int sortOrder) {
			ContentValues values = new ContentValues();
			values.put(DatabaseDefs.ROUTEID,   routeId);
			values.put(DatabaseDefs.CAMERAID,  cameraId);
			values.put(DatabaseDefs.SORTORDER, sortOrder);
			db.insert(DatabaseDefs.CAMERASINROUTE_TABLE_NAME, null, values);
		}

		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			Log.w(TAG, "Upgrading database from version " + oldVersion + " to "
					+ newVersion + ", which will destroy all old data");
			db.execSQL("DROP TABLE IF EXISTS " + DatabaseDefs.CAMERAS_TABLE_NAME);
			db.execSQL("DROP TABLE IF EXISTS " + DatabaseDefs.COUNTY_TABLE_NAME);
			db.execSQL("DROP TABLE IF EXISTS " + DatabaseDefs.ROUTES_TABLE_NAME);
			db.execSQL("DROP TABLE IF EXISTS " + DatabaseDefs.CAMERASINROUTE_TABLE_NAME);
			onCreate(db);
		}
	}

	private DatabaseHelper dbHelper;

	@Override
	public int delete(Uri uri, String where, String[] whereArgs) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count;
		switch (sUriMatcher.match(uri)) {
		case CAMERAS:
			count = db.delete(DatabaseDefs.CAMERAS_TABLE_NAME, where, whereArgs);
			break;

		case COUNTIES:
			throw new IllegalArgumentException("Delete counties not allowed.");
		case ROUTES:
			count = db.delete(DatabaseDefs.ROUTES_TABLE_NAME, where, whereArgs);
		case CAMERAROUTES:
			count = db.delete(DatabaseDefs.CAMERASINROUTE_TABLE_NAME, where, whereArgs);
			uri = CONTENT_URI; // for change notification
			break;
			
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
		
		getContext().getContentResolver().notifyChange(uri, null);
		return count;

	}

	@Override
	public String getType(Uri uri) {
		switch (sUriMatcher.match(uri)) {
		case CAMERAS:
			return CONTENT_TYPE;
			
		case COUNTIES:
			return CONTENT_TYPE; // TODO define content type
		
		case ROUTES:
			return CONTENT_TYPE; // TODO define content type
		
		case CAMERAROUTES:
			return CONTENT_TYPE; // TODO define content type
			
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
	}

	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		// TODO use case
		if (sUriMatcher.match(uri) == COUNTIES) {
			throw new IllegalArgumentException("Insertion of counties not allowed.");
		}
		
		if (sUriMatcher.match(uri) == ROUTES) {
			return insertInTable(uri, initialValues, DatabaseDefs.ROUTES_TABLE_NAME, ROUTES_URI);
		}
		
		if (sUriMatcher.match(uri) == CAMERAS) {
			return insertInTable(uri, initialValues, DatabaseDefs.CAMERAS_TABLE_NAME, CONTENT_URI);
		}
		
		if (sUriMatcher.match(uri) == CAMERAROUTES) {
			return insertInTable(uri, initialValues, DatabaseDefs.CAMERASINROUTE_TABLE_NAME, CAMERAROUTES_URI);
		}
		
		throw new IllegalArgumentException("Unknown URI " + uri);
	}

	private Uri insertInTable(Uri uri, ContentValues initialValues,
			String tableName, Uri contentUri) {
		ContentValues values;
		if (initialValues != null) {
			values = new ContentValues(initialValues);
		} else {
			values = new ContentValues();
		}

		SQLiteDatabase db = dbHelper.getWritableDatabase();
		long rowId = db.insert(tableName, "", values);
		if (rowId > 0) {
			Uri noteUri = ContentUris.withAppendedId(contentUri, rowId);
			getContext().getContentResolver().notifyChange(noteUri, null);
			return noteUri;
		}

		throw new SQLException("Failed to insert row into " + uri);
	}

	@Override
	public boolean onCreate() {
		dbHelper = new DatabaseHelper(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {
		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();

		switch (sUriMatcher.match(uri)) {
		case CAMERAS:
			qb.setTables(DatabaseDefs.CAMERAS_TABLE_NAME);
			qb.setProjectionMap(camerasProjectionMap);
			break;
		case COUNTIES:
			qb.setTables(DatabaseDefs.COUNTY_TABLE_NAME);
			qb.setProjectionMap(camerasProjectionMap); // TODO projection map
			break;
		case ROUTES:
			qb.setTables(DatabaseDefs.ROUTES_TABLE_NAME);
			qb.setProjectionMap(camerasProjectionMap); // TODO projection map
			break;
		case CAMERAROUTES:
			qb.setTables(DatabaseDefs.CAMERASINROUTE_TABLE_NAME);
			qb.setProjectionMap(camerasProjectionMap); // TODO projection map
			break;

		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}

		SQLiteDatabase db = dbHelper.getWritableDatabase(); // TODO should be readable
		
		// Ugly hack to count number of rows...
		Cursor c;
		if (projection.length > 0 && projection[0].contentEquals("count(*)"))
			c = db.query(qb.getTables(), // Table
				projection, // Columns
                selection, // Selection
                selectionArgs, // Selection arguments
                null, // GROUP BY
                null, // HAVING
                sortOrder); // ORDER BY
		else
			c = qb.query(db, projection, selection, selectionArgs, null,
				null, sortOrder);
		
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;

	}

	@Override
	public int update(Uri uri, ContentValues values, String where,
			String[] whereArgs) {
		SQLiteDatabase db = dbHelper.getWritableDatabase();
		int count;
		switch (sUriMatcher.match(uri)) {
		case CAMERAS:
			count = db.update(DatabaseDefs.CAMERAS_TABLE_NAME, values, where, whereArgs);
			break;

		case COUNTIES:
			throw new IllegalArgumentException("Update counties not allowed.");
		case ROUTES:
			count = db.update(DatabaseDefs.ROUTES_TABLE_NAME, values, where, whereArgs);
			break;
		case CAMERAROUTES:
			throw new IllegalArgumentException("Update camera to route connection not implemented.");
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}

		getContext().getContentResolver().notifyChange(uri, null);
		return count;

	}

	static {
		sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
		sUriMatcher.addURI(CameraDefs.AUTHORITY, CameraDefs.CAMERAS, CAMERAS);
		sUriMatcher.addURI(CameraDefs.AUTHORITY, CameraDefs.COUNTIES, COUNTIES);
		sUriMatcher.addURI(CameraDefs.AUTHORITY, CameraDefs.ROUTES, ROUTES);
		sUriMatcher.addURI(CameraDefs.AUTHORITY, CameraDefs.CAMERAROUTES, CAMERAROUTES);

		camerasProjectionMap = new HashMap<String, String>();
		camerasProjectionMap.put(CameraDefs.ID,               DatabaseDefs.ID);
		camerasProjectionMap.put(CameraDefs.NAME,             DatabaseDefs.NAME);
		camerasProjectionMap.put(CameraDefs.COUNTY,           DatabaseDefs.COUNTY);
		camerasProjectionMap.put(CameraDefs.ROAD,             DatabaseDefs.ROAD);
		camerasProjectionMap.put(CameraDefs.DESCRIPTION,      DatabaseDefs.DESCRIPTION);
		camerasProjectionMap.put(CameraDefs.CAMERA_URL,       DatabaseDefs.CAMERA_URL);
		camerasProjectionMap.put(CameraDefs.MAP_URL,          DatabaseDefs.MAP_URL);
		camerasProjectionMap.put(CameraDefs.LONGITUDE,        DatabaseDefs.LONGITUDE);
		camerasProjectionMap.put(CameraDefs.LATITUDE,         DatabaseDefs.LATITUDE);
		camerasProjectionMap.put(CameraDefs.FAVORITE,         DatabaseDefs.FAVORITE);
		camerasProjectionMap.put(CameraDefs.EXTID,            DatabaseDefs.EXTID);
		camerasProjectionMap.put(CameraDefs.CAMERA_URL_SMALL, DatabaseDefs.CAMERA_URL_SMALL);
		camerasProjectionMap.put(CameraDefs.NICKNAME,         DatabaseDefs.NICKNAME);

	}
}
